refactor garmin_fs class (#1203)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Wed, 1 Nov 2023 13:16:44 +0000 (07:16 -0600)
committerGitHub <noreply@github.com>
Wed, 1 Nov 2023 13:16:44 +0000 (07:16 -0600)
* use container for garmin_fs ilinks.

and leave memory management up to the copy on write container.

* make gmsd const for read only usage.

this prevents the possiblity of detachment of the ilinks list.

* incorporate some variables and functions into garmin_fs_t

* unify garmin category conversions.

* fiddle with test time zone sensitivity.

* add utc option for garmin_txt reader.

* correct sign of adjustment

* update garmin_txt includes.

16 files changed:
garmin.cc
garmin_fs.cc
garmin_fs.h
garmin_gpi.cc
garmin_txt.cc
gdb.cc
gdb.h
gpx.cc
random.cc
reference/garmincategories.gpx [new file with mode: 0644]
reference/garmincategories.txt [new file with mode: 0644]
testo.d/gpx.test
unicsv.cc
util.cc
waypt.cc
xcsv.cc

index 1382f9eadc7e219d5dcd9526809368cd23915a8c..dd04edc424ac67e7c4999f21d589fffd926ce992 100644 (file)
--- a/garmin.cc
+++ b/garmin.cc
@@ -1241,7 +1241,7 @@ d103_icon_number_from_symbol(const QString& s)
 static void
 garmin_fs_garmin_after_read(const GPS_PWay way, Waypoint* wpt, const int protoid)
 {
-  garmin_fs_t* gmsd = garmin_fs_alloc(protoid);
+  auto* gmsd = new garmin_fs_t(protoid);
   wpt->fs.FsChainAdd(gmsd);
 
   /* nothing happens until gmsd is allocated some lines above */
@@ -1275,7 +1275,7 @@ garmin_fs_garmin_after_read(const GPS_PWay way, Waypoint* wpt, const int protoid
 static void
 garmin_fs_garmin_before_write(const Waypoint* wpt, GPS_PWay way, const int protoid)
 {
-  garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+  const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
   (void)protoid; // unused for now.
 
index 148db91693aced43a026fa1e2c6a0580b31f3c08..525523005b2952099c12d147df95c93954183e6d 100644 (file)
 #include "inifile.h"  // for inifile_readstr
 
 
-#define MYNAME "garmin_fs"
-
-garmin_fs_t*
-garmin_fs_alloc(const int protocol)
-{
-  auto* result = new garmin_fs_t;
-
-  result->protocol = protocol;
-
-  return result;
-}
-
-garmin_fs_t* garmin_fs_t::clone() const
+std::optional<uint16_t>
+garmin_fs_t::convert_category(const QString& category_name)
 {
-  auto* copy = new garmin_fs_t(*this);
+  std::optional<uint16_t> category;
 
-  /* do not deep copy interlinks, only increment the reference counter */
-  if (ilinks != nullptr) {
-    ilinks->ref_count++;
-  }
-
-#ifdef GMSD_EXPERIMENTAL
-  memcopy(subclass, other.subclass, sizeof(subclass));
-#endif
-
-  return copy;
-}
-
-garmin_fs_t::~garmin_fs_t()
-{
-  garmin_ilink_t* links;
-  if ((links = ilinks) != nullptr) {
-    links->ref_count--;
-    if (links->ref_count <= 0) {
-      while (links != nullptr) {
-        garmin_ilink_t* tmp = links;
-        links = links->next;
-        xfree(tmp);
-      }
-    }
-  }
-}
-
-bool
-garmin_fs_convert_category(const QString& category_name, uint16_t* category)
-{
   // Is the name  "Category" followed by a number? Use that number.
   if (category_name.startsWith(u"Category ", Qt::CaseInsensitive)) {
     bool ok;
     int i = category_name.mid(9).toInt(&ok);
     if (ok && (i >= 1) && (i <= 16)) {
-      *category = (1 << --i);
-      return true;
+      category = (1 << --i);
+      return category;
     }
   }
   if (global_opts.inifile != nullptr) {
     // Do we have a gpsbabel.ini that maps category names to category #'s?
     for (int i = 0; i < 16; i++) {
       QString key = QString::number(i + 1);
-      QString c = inifile_readstr(global_opts.inifile, GMSD_SECTION_CATEGORIES, key);
+      QString c = inifile_readstr(global_opts.inifile, kGmsdSectionCategories, key);
       if (c.compare(category_name, Qt::CaseInsensitive) == 0) {
-        *category = (1 << i);
-        return true;
+        category = (1 << i);
+        return category;
       }
     }
   }
-  return false;
+  return category;
 }
 
-bool
-garmin_fs_merge_category(const QString& category_name, Waypoint* waypt)
+QStringList
+garmin_fs_t::print_categories(uint16_t categories)
 {
-  uint16_t cat;
+  QStringList categoryList;
 
-  // Attempt to get a textual category name to a category number.
-  if (!garmin_fs_convert_category(category_name, &cat)) {
-    return false;
+  if (categories == 0) {
+    return categoryList;
   }
 
-  garmin_fs_t* gmsd = garmin_fs_t::find(waypt);
-  cat = cat | (garmin_fs_t::get_category(gmsd, 0));
+  for (int i = 0; i < 16; i++) {
+    if ((categories & 1) != 0) {
+      QString c;
+      if (global_opts.inifile != nullptr) {
+        QString key = QString::number(i + 1);
+        c = inifile_readstr(global_opts.inifile, kGmsdSectionCategories, key);
+      }
 
-  if (gmsd == nullptr) {
-    gmsd = garmin_fs_alloc(-1);
-    waypt->fs.FsChainAdd(gmsd);
+      if (c.isNull()) {
+        categoryList << QString::asprintf("Category %d", i+1);
+      }
+//                             *fout << QString::asprintf("%s", gps_categories[i]);
+      else {
+        categoryList << c;
+      }
+
+    }
+    categories = categories >> 1;
   }
-  garmin_fs_t::set_category(gmsd, cat);
-  return true;
+  return categoryList;
 }
index 0535d85aca63d82eae044ae68cdc9adba03c4e9f..ac74e89576ab6fc1213b3a931a77613bea90b6f5 100644 (file)
@@ -25,7 +25,9 @@
 #define GARMIN_FS_H
 
 #include <cstdint>     // for int32_t, int16_t, uint16_t
+#include <optional>    // for optional
 
+#include <QList>       // for QList
 #include <QString>     // for QString
 
 #include "defs.h"
@@ -41,9 +43,9 @@
 */
 
 struct garmin_ilink_t {
-  int ref_count;
-  double lat, lon, alt;
-  garmin_ilink_t* next;
+  double lat;
+  double lon;
+  double alt;
 };
 
 struct garmin_fs_flags_t {
@@ -95,6 +97,8 @@ public:
 
 class garmin_fs_t : public FormatSpecificData {
 public:
+  /* Data Members */
+
   garmin_fs_flags_t flags;
 
   int protocol{0};             /* ... used by device (-1 is MapSource) */
@@ -117,26 +121,30 @@ public:
   QString email;                               /* email address */
   unsigned int duration; /* expected travel time to next route point, in seconds, only when auto-routed */
 
-  garmin_ilink_t* ilinks{nullptr};
+  QList<garmin_ilink_t> ilinks;
 #ifdef GMSD_EXPERIMENTAL
   char subclass[22]{};
 #endif
 
-public:
+  /* Special Member Functions */
+
   garmin_fs_t() : FormatSpecificData(kFsGmsd) {}
-private:
-  garmin_fs_t(const garmin_fs_t& other) = default;
-public:
-  garmin_fs_t& operator=(const garmin_fs_t& rhs) = delete; /* not implemented */
-  garmin_fs_t(garmin_fs_t&&) = delete; /* not implemented */
-  garmin_fs_t& operator=(garmin_fs_t&&) = delete; /* not implemented */
-  ~garmin_fs_t() override;
+  explicit garmin_fs_t(int p) : garmin_fs_t() {protocol = p;}
+
+  /* Member Functions */
+
+  garmin_fs_t* clone() const override
+  {
+    return new garmin_fs_t(*this);
+  }
 
-  garmin_fs_t* clone() const override;
   static garmin_fs_t* find(const Waypoint* wpt) {
     return reinterpret_cast<garmin_fs_t*>(wpt->fs.FsChainFind(kFsGmsd));
   }
 
+  static std::optional<uint16_t> convert_category(const QString& category_name);
+  static QStringList print_categories(uint16_t categories);
+
 #define GEN_GMSD_METHODS(field) \
   static bool has_##field(const garmin_fs_t* gmsd) \
   { \
@@ -212,18 +220,10 @@ public:
   GEN_GMSD_STR_METHODS(email)
 
 #undef GEN_GMSD_STR_METHODS
-};
-
-garmin_fs_t* garmin_fs_alloc(int protocol);
-void garmin_fs_destroy(void* fs);
-void garmin_fs_copy(void** dest, const void* src);
-
-/* ..convert_category: returns true=OK; false=Unable to convert category */
-bool garmin_fs_convert_category(const QString& category_name, uint16_t* category);
 
-/* ..merge_category: returns true=OK; false=Unable to convert category */
-bool garmin_fs_merge_category(const QString& category_name, Waypoint* waypt);
-
-#define GMSD_SECTION_CATEGORIES "Garmin Categories"
+private:
+  /* Constants */
 
+  static constexpr char kGmsdSectionCategories[] = "Garmin Categories";
+};
 #endif
index 340c823be36efdf1ba1f5fe9448cbcbc31577609..3f9a824fbacbac96e0da25f39f4e26d33eb3d9ec 100644 (file)
@@ -41,7 +41,7 @@
 
 #include "defs.h"
 #include "formspec.h"              // for FormatSpecificDataList
-#include "garmin_fs.h"             // for garmin_fs_t, garmin_fs_alloc
+#include "garmin_fs.h"             // for garmin_fs_t
 #include "gbfile.h"                // for gbfputint32, gbfgetint32, gbfgetint16, gbfputint16, gbfgetc, gbfputc, gbfread, gbftell, gbfwrite, gbfseek, gbfclose, gbfopen_le, gbfgetuint16, gbsize_t, gbfile
 #include "jeeps/gpsmath.h"         // for GPS_Math_Deg_To_Semi, GPS_Math_Semi_To_Deg
 
@@ -76,7 +76,7 @@ GarminGPIFormat::gpi_gmsd_init(Waypoint* wpt)
   }
   garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
   if (gmsd == nullptr) {
-    gmsd = garmin_fs_alloc(-1);
+    gmsd = new garmin_fs_t(-1);
     wpt->fs.FsChainAdd(gmsd);
   }
   return gmsd;
@@ -700,7 +700,7 @@ GarminGPIFormat::wdata_compute_size(writer_data_t* data) const
   res = 23;  /* bounds, ... of tag 0x80008 */
 
   foreach (Waypoint* wpt, data->waypt_list) {
-    garmin_fs_t* gmsd;
+    const garmin_fs_t* gmsd;
 
     res += 12;    /* tag/sz/sub-sz */
     res += 19;    /* poi fixed size */
index c49569445a8aa5d433ae3470131373bbb3c352c5..8a6a1433f4432a6c87b16e27720026f575cb6b04 100644 (file)
@@ -33,6 +33,7 @@
 #include <cstdlib>                 // for abs
 #include <cstring>                 // for strstr, strlen
 #include <ctime>                   // for time_t, gmtime, localtime, strftime
+#include <optional>                // for optional
 #include <utility>                 // for pair, make_pair
 
 #include <QByteArray>              // for QByteArray
@@ -49,9 +50,8 @@
 
 #include "csv_util.h"              // for csv_linesplit
 #include "formspec.h"              // for FormatSpecificDataList
-#include "garmin_fs.h"             // for garmin_fs_t, garmin_fs_alloc, garmin_fs_convert_category, GMSD_SECTION_CATEGORIES
+#include "garmin_fs.h"             // for garmin_fs_t
 #include "garmin_tables.h"         // for gt_display_modes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_get_mps_grid_longname, gt_lookup_datum_index, gt_lookup_grid_type, GDB, gt_get_icao_cc, gt_get_icao_country, gt_get_mps_datum_name, gt_waypt_class_names, GT_DISPLAY_MODE...
-#include "inifile.h"               // for inifile_readstr
 #include "jeeps/gpsmath.h"         // for GPS_Math_Known_Datum_To_UTM_EN, GPS_Math_WGS84_To_Known_Datum_M, GPS_Math_WGS84_To_Swiss_EN, GPS_Math_WGS84_To_UKOSMap_M
 #include "src/core/datetime.h"     // for DateTime
 #include "src/core/logging.h"      // for Fatal
@@ -221,7 +221,7 @@ convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon)
 static void
 enum_waypt_cb(const Waypoint* wpt)
 {
-  garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+  const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
   int wpt_class = garmin_fs_t::get_wpt_class(gmsd, 0);
   if (wpt_class < 0x80) {
     if (gtxt_flags.enum_waypoints) {           /* enumerate only */
@@ -393,30 +393,9 @@ print_date_and_time(const time_t time, const bool time_only)
 static void
 print_categories(uint16_t categories)
 {
-  if (categories == 0) {
-    return;
-  }
-
-  int count = 0;
-  for (int i = 0; i < 16; i++) {
-    if ((categories & 1) != 0) {
-      QString c;
-      if (global_opts.inifile != nullptr) {
-        QString key = QString::number(i + 1);
-        c = inifile_readstr(global_opts.inifile, GMSD_SECTION_CATEGORIES, key);
-      }
-
-      *fout << QString::asprintf("%s", (count++ > 0) ? "," : "");
-      if (c.isNull()) {
-        *fout << QString::asprintf("Category %d", i+1);
-      }
-//                             *fout << QString::asprintf("%s", gps_categories[i]);
-      else {
-        *fout << c;
-      }
-
-    }
-    categories = categories >> 1;
+  const QStringList categoryList = garmin_fs_t::print_categories(categories);
+  if (!categoryList.isEmpty()) {
+    *fout << categoryList.join(',');
   }
 }
 
@@ -528,7 +507,7 @@ write_waypt(const Waypoint* wpt)
 {
   const char* wpt_type;
 
-  garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+  const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
   int i = garmin_fs_t::get_display(gmsd, 0);
   if (i > GT_DISPLAY_MODE_MAX) {
@@ -740,6 +719,28 @@ track_disp_wpt_cb(const Waypoint* wpt)
 * %%%        global callbacks called by gpsbabel main process              %%% *
 *******************************************************************************/
 
+static void
+garmin_txt_utc_option()
+{
+  if (opt_utc != nullptr) {
+    if (case_ignore_strcmp(opt_utc, "utc") == 0) {
+      utc_offs = 0;
+    } else {
+      utc_offs = xstrtoi(opt_utc, nullptr, 10);
+    }
+    utc_offs *= (60 * 60);
+    gtxt_flags.utc = 1;
+  }
+}
+
+static void
+garmin_txt_adjust_time(QDateTime& dt)
+{
+  if (gtxt_flags.utc) {
+    dt = dt.toUTC().addSecs(dt.offsetFromUtc() - utc_offs);
+  }
+}
+
 static void
 garmin_txt_wr_init(const QString& fname)
 {
@@ -786,15 +787,7 @@ garmin_txt_wr_init(const QString& fname)
     datum_index = gt_lookup_datum_index(datum_str, MYNAME);
   }
 
-  if (opt_utc != nullptr) {
-    if (case_ignore_strcmp(opt_utc, "utc") == 0) {
-      utc_offs = 0;
-    } else {
-      utc_offs = xstrtoi(opt_utc, nullptr, 10);
-    }
-    utc_offs *= (60 * 60);
-    gtxt_flags.utc = 1;
-  }
+  garmin_txt_utc_option();
 }
 
 static void
@@ -902,7 +895,7 @@ strftime_to_timespec(const char* s)
           q += "yyyy";
           continue;
         case 'H':
-          q += "hh";
+          q += "HH";
           continue;
         case 'M':
           q += "mm";
@@ -931,8 +924,11 @@ strftime_to_timespec(const char* s)
         case 'F':
           q += "yyyy-MM-dd";
           continue;
+        case 'p':
+          q += "AP";
+          continue;
         default:
-          q += s[i+1];
+          warning(MYNAME ": omitting unknown strptime conversion \"%%%c\" in \"%s\"\n", s[i], s);
           break;
         }
       }
@@ -964,11 +960,10 @@ parse_categories(const QString& str)
   for (const auto& catstring : catstrings) {
     QString cin = catstring.trimmed();
     if (!cin.isEmpty()) {
-      uint16_t val;
-      if (!garmin_fs_convert_category(cin, &val)) {
+      if (std::optional<uint16_t> cat = garmin_fs_t::convert_category(cin); !cat.has_value()) {
         warning(MYNAME ": Unable to convert category \"%s\" at line %d!\n", qPrintable(cin), current_line);
       } else {
-        res = res | val;
+        res = res | *cat;
       }
     }
   }
@@ -1102,7 +1097,7 @@ parse_waypoint(const QStringList& lineparts)
   bind_fields(waypt_header);
 
   auto* wpt = new Waypoint;
-  garmin_fs_t* gmsd = garmin_fs_alloc(-1);
+  auto* gmsd = new garmin_fs_t(-1);
   wpt->fs.FsChainAdd(gmsd);
 
   for (const auto& str : lineparts) {
@@ -1184,6 +1179,7 @@ parse_waypoint(const QStringList& lineparts)
       break;
     case 16:
       if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+        garmin_txt_adjust_time(dt);
         wpt->SetCreationTime(dt);
       }
     break;
@@ -1319,6 +1315,7 @@ parse_track_waypoint(const QStringList& lineparts)
       break;
     case 2:
       if (QDateTime dt = parse_date_and_time(str); dt.isValid()) {
+        garmin_txt_adjust_time(dt);
         wpt->SetCreationTime(dt);
       }
     break;
@@ -1367,6 +1364,7 @@ garmin_txt_rd_init(const QString& fname)
   grid_index = (grid_type) -1;
 
   init_date_and_time_format();
+  garmin_txt_utc_option();
 }
 
 static void
diff --git a/gdb.cc b/gdb.cc
index 5efbb55f33a8ad0d9d1d4881c6ca197899642826..db3bec6a58febb265629371446a6ad7dc09e9571 100644 (file)
--- a/gdb.cc
+++ b/gdb.cc
@@ -42,7 +42,7 @@
 
 #include "defs.h"                   // for Waypoint, warning, route_head, fatal, UrlLink, bounds, UrlList, unknown_alt, xfree, waypt_add_to_bounds, waypt_init_bounds, xstrtoi, route_add_wpt, route_disp_all, waypt_bounds_valid, xmalloc, gb_color, WaypointList, find_wa...
 #include "formspec.h"               // for FormatSpecificDataList
-#include "garmin_fs.h"              // for garmin_fs_t, garmin_ilink_t, garmin_fs_alloc
+#include "garmin_fs.h"              // for garmin_fs_t, garmin_ilink_t
 #include "garmin_tables.h"          // for gt_waypt_class_map_point, gt_color_index_by_rgb, gt_color_value, gt_waypt_classes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_gdb_display_mode_symbol, gt_get_icao_country, gt_waypt_class_user_waypoint, GDB, gt_display_mode_symbol
 #include "gbfile.h"                 // for gbfgetint32, gbfputint32, gbfgetc, gbfread, gbfwrite, gbfgetdbl, gbfputc, gbfgetcstr, gbfclose, gbfgetnativecstr, gbfopen_le, gbfputint16, gbfile, gbfcopyfrom, gbfputcstr, gbfrewind, gbfseek, gbftell, gbfgetcstr_old, gbfgetint16, gbfgetuint32, gbfputdbl
 #include "grtcirc.h"                // for RAD, gcdist, radtometers
@@ -440,7 +440,7 @@ GdbFormat::read_waypoint(gt_waypt_classes_e* waypt_class_out)
   waypt_ct++;
   res = new Waypoint;
 
-  gmsd = garmin_fs_alloc(-1);
+  gmsd = new garmin_fs_t(-1);
   res->fs.FsChainAdd(gmsd);
   res->shortname = fread_cstr();
   wpt_class = (gt_waypt_classes_e) FREAD_i32;
@@ -738,46 +738,37 @@ GdbFormat::read_route()
     }
 
     int links = FREAD_i32;
-    garmin_ilink_t* il_anchor = nullptr;
-    garmin_ilink_t* il_root = nullptr;
+    QList<garmin_ilink_t> il_list;
 #if GDB_DEBUG
     DBG(GDB_DBG_RTE, links)
     printf(MYNAME "-rte_pt \"%s\" (%d): %d interlink step(s)\n",
            qPrintable(wpt->shortname), wpt_class, links);
 #endif
     for (int j = 0; j < links; j++) {
-      auto* il_step = (garmin_ilink_t*) xmalloc(sizeof(garmin_ilink_t));
+      garmin_ilink_t il_step;
 
-      il_step->ref_count = 1;
-
-      il_step->lat = FREAD_LATLON;
-      il_step->lon = FREAD_LATLON;
+      il_step.lat = FREAD_LATLON;
+      il_step.lon = FREAD_LATLON;
       if (FREAD_C == 1) {
-        il_step->alt = FREAD_DBL;
+        il_step.alt = FREAD_DBL;
       } else {
-        il_step->alt = unknown_alt;
+        il_step.alt = unknown_alt;
       }
 
       if (j == 0) {
-        wpt->latitude = il_step->lat;
-        wpt->longitude = il_step->lon;
-        wpt->altitude = il_step->alt;
+        wpt->latitude = il_step.lat;
+        wpt->longitude = il_step.lon;
+        wpt->altitude = il_step.alt;
       }
 
-      il_step->next = nullptr;
-      if (il_anchor == nullptr) {
-        il_root = il_step;
-      } else {
-        il_anchor->next = il_step;
-      }
-      il_anchor = il_step;
+      il_list.append(il_step);
 
 #if GDB_DEBUG
       DBG(GDB_DBG_RTEe, true) {
         printf(MYNAME "-rte_il \"%s\" (%d of %d): %c%0.6f %c%0.6f\n",
                qPrintable(wpt->shortname), j + 1, links,
-               il_step->lat < 0 ? 'S' : 'N', il_step->lat,
-               il_step->lon < 0 ? 'W' : 'E', il_step->lon);
+               il_step.lat < 0 ? 'S' : 'N', il_step.lat,
+               il_step.lon < 0 ? 'W' : 'E', il_step.lon);
       }
 #endif
     }
@@ -832,19 +823,13 @@ GdbFormat::read_route()
     if (wpt != nullptr) {
       garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       if (gmsd == nullptr) {
-        gmsd = garmin_fs_alloc(-1);
+        gmsd = new garmin_fs_t(-1);
         wpt->fs.FsChainAdd(gmsd);
       }
       garmin_fs_t::set_wpt_class(gmsd, wpt_class);
-      gmsd->ilinks = il_root;
-      il_root = nullptr;
+      gmsd->ilinks = il_list;
     }
 
-    while (il_root) {
-      garmin_ilink_t* il = il_root;
-      il_root = il_root->next;
-      xfree(il);
-    }
   } /* ENDFOR: for (i = 0; i < points; i++) */
 
   /* VERSION DEPENDENT CODE */
@@ -1194,7 +1179,7 @@ GdbFormat::gdb_check_waypt(Waypoint* wpt)
 
 void
 GdbFormat::write_waypoint(
-  const Waypoint* wpt, const QString& shortname, garmin_fs_t* gmsd,
+  const Waypoint* wpt, const QString& shortname, const garmin_fs_t* gmsd,
   const int icon, const int display)
 {
   char zbuf[32], ffbuf[32];
@@ -1391,7 +1376,7 @@ GdbFormat::write_route(const route_head* rte, const QString& rte_name)
       fatal(MYNAME ": Sorry, that should never happen!!!\n");
     }
 
-    garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+    const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
     /* extra_data may contain a modified shortname */
     gdb_write_cstr((wpt->extra_data) ? *static_cast<QString*>(wpt->extra_data) : wpt->shortname);
@@ -1564,7 +1549,7 @@ GdbFormat::write_waypoint_cb(const Waypoint* refpt)
     fout = ftmp;
 
     /* prepare the waypoint */
-    garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+    const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
     int wpt_class = garmin_fs_t::get_wpt_class(gmsd, -1);
     if (wpt_class == -1) {
diff --git a/gdb.h b/gdb.h
index 6697ca592e62c59d86f9007881799f5387436559..fc349740195136f5216b3f38d345afab8cef72b8 100644 (file)
--- a/gdb.h
+++ b/gdb.h
@@ -150,7 +150,7 @@ private:
   void reset_short_handle(const char* defname);
   void write_header();
   static void gdb_check_waypt(Waypoint* wpt);
-  void write_waypoint(const Waypoint* wpt, const QString& shortname, garmin_fs_t* gmsd, int icon, int display);
+  void write_waypoint(const Waypoint* wpt, const QString& shortname, const garmin_fs_t* gmsd, int icon, int display);
   static void route_compute_bounds(const route_head* rte, bounds* bounds);
   void route_write_bounds(bounds* bounds) const;
   void write_route(const route_head* rte, const QString& rte_name);
diff --git a/gpx.cc b/gpx.cc
index de176e454d16391e2eb6cc4bcbe2e5be3ee73adf..176ecc67625b304ebfb75d7c49a7c9af84140b0e 100644 (file)
--- a/gpx.cc
+++ b/gpx.cc
@@ -47,7 +47,7 @@
 #include <QtGlobal>                         // for qAsConst, QAddConst<>::Type
 
 #include "defs.h"
-#include "garmin_fs.h"                      // for garmin_fs_t, garmin_ilink_t, garmin_fs_alloc, garmin_fs_merge_category
+#include "garmin_fs.h"                      // for garmin_fs_t, garmin_ilink_t
 #include "garmin_tables.h"                  // for gt_color_index_by_rgb, gt_color_name, gt_color_value_by_name
 #include "geocache.h"                       // for Geocache, Geocache::UtfSt...
 #include "mkshort.h"                        // for MakeShort
@@ -218,7 +218,7 @@ GpxFormat::tag_garmin_fs(tag_type tag, const QString& text, Waypoint* waypt)
 {
   garmin_fs_t* gmsd = garmin_fs_t::find(waypt);
   if (gmsd == nullptr) {
-    gmsd = garmin_fs_alloc(-1);
+    gmsd = new garmin_fs_t(-1);
     waypt->fs.FsChainAdd(gmsd);
   }
 
@@ -243,7 +243,10 @@ GpxFormat::tag_garmin_fs(tag_type tag, const QString& text, Waypoint* waypt)
     }
     break;
   case tag_type::garmin_wpt_category:
-    if (!garmin_fs_merge_category(text, waypt)) {
+    if (auto cat = garmin_fs_t::convert_category(text); cat.has_value()) {
+      cat = *cat | (garmin_fs_t::get_category(gmsd, 0));
+      garmin_fs_t::set_category(gmsd, *cat);
+    } else {
       // There's nothing a user can really do about this (well, they could
       // create a gpsbabel.ini that mapped them to garmin category numbers
       // but that feature is so obscure and used in so few outputs that
@@ -1346,7 +1349,7 @@ GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_poin
     // Although not required by the schema we assume that gpxx:RoutePointExtension must be a child of gpx:rtept.
     // Although not required by the schema we assume that gpxx:TrackPointExtension must be a child of gpx:trkpt.
     // Although not required by the schema we assume that gpxtpx:TrackPointExtension must be a child of gpx:trkpt.
-    garmin_fs_t* gmsd = garmin_fs_t::find(waypointp);
+    const garmin_fs_t* gmsd = garmin_fs_t::find(waypointp);
     switch (point_type) {
     case gpxpt_waypoint:
       writer->stackOptionalStartElement(QStringLiteral("gpxx:WaypointExtension"));
@@ -1379,11 +1382,9 @@ GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_poin
       if (garmin_fs_t::has_category(gmsd)) {
         uint16_t cx = gmsd->category;
         writer->stackStartElement(QStringLiteral("gpxx:Categories"));
-        for (int i = 0; i < 16; i++) {
-          if (cx & 1) {
-            writer->stackTextElement(QStringLiteral("gpxx:Category"), QStringLiteral("Category %1").arg(i+1));
-          }
-          cx = cx >> 1;
+        const QStringList categoryList = garmin_fs_t::print_categories(cx);
+        for (const auto& text : categoryList) {
+            writer->stackTextElement(QStringLiteral("gpxx:Category"), text);
         }
         writer->stackEndElement(); // gpxx:Categories
       }
@@ -1401,19 +1402,21 @@ GpxFormat::gpx_write_common_extensions(const Waypoint* waypointp, const gpx_poin
       writer->stackEndElement(); // gpxx:WaypointExtension
       break;
     case gpxpt_route:
-      if (gmsd != nullptr && gmsd->ilinks != nullptr) {
+      if (gmsd != nullptr && !gmsd->ilinks.isEmpty()) {
         writer->stackOptionalStartElement(QStringLiteral("gpxx:RoutePointExtension"));
-        garmin_ilink_t* link = gmsd->ilinks;
-        garmin_ilink_t* prior = nullptr; // GDB files sometime contain repeated point; omit them
-        while (link != nullptr) {
-          if (prior == nullptr || prior->lat != link->lat || prior->lon != link->lon) {
+        double prior_lat; // GDB files sometime contain repeated point; omit them
+        double prior_lon;
+        bool first = true;
+        for (const auto& link : gmsd->ilinks) {
+          if (first || (prior_lat != link.lat) || (prior_lon != link.lon)) {
             writer->stackStartElement(QStringLiteral("gpxx:rpt"));
-            writer->stackAttribute(QStringLiteral("lat"), toString(link->lat));
-            writer->stackAttribute(QStringLiteral("lon"), toString(link->lon));
+            writer->stackAttribute(QStringLiteral("lat"), toString(link.lat));
+            writer->stackAttribute(QStringLiteral("lon"), toString(link.lon));
             writer->stackEndElement(); // "gpxx:rpt"
+            prior_lat = link.lat;
+            prior_lon = link.lon;
+            first = false;
           }
-          prior = link;
-          link = link->next;
         }
         writer->stackEndElement(); // gpxx:RoutePointExtension
       }
index 7026a9e53516965051f8592e9ef4910ae7c95311..339c0460234f5dcbad0c1b14c975f0fde63f9954 100644 (file)
--- a/random.cc
+++ b/random.cc
@@ -28,7 +28,7 @@
 #include "defs.h"
 #include "random.h"
 #include "formspec.h"           // for FormatSpecificDataList
-#include "garmin_fs.h"          // for garmin_fs_t, GMSD_SET, garmin_fs_flags_t, garmin_fs_alloc
+#include "garmin_fs.h"          // for garmin_fs_t, GMSD_SET, garmin_fs_flags_t
 #include "src/core/datetime.h"  // for DateTime
 
 
@@ -104,7 +104,7 @@ Waypoint*
 RandomFormat::random_generate_wpt(int i, const QDateTime& time, const Waypoint* prev)
 {
   auto* wpt = new Waypoint;
-  garmin_fs_t* gmsd = garmin_fs_alloc(-1);
+  auto* gmsd = new garmin_fs_t(-1);
   wpt->fs.FsChainAdd(gmsd);
 
   do {
diff --git a/reference/garmincategories.gpx b/reference/garmincategories.gpx
new file mode 100644 (file)
index 0000000..95b140d
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.1" creator="GPSBabel - https://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1">
+  <metadata>
+    <time>1970-01-01T00:00:00Z</time>
+    <bounds minlat="39.973869717" minlon="-105.498962400" maxlat="40.003967283" maxlon="-105.465850367"/>
+  </metadata>
+  <wpt lat="39.973869717" lon="-105.465850367">
+    <time>2013-03-09T20:45:12Z</time>
+    <name>Hwy 119</name>
+    <cmt>The Diagonal</cmt>
+    <desc>The Diagonal</desc>
+    <sym>Flag, Blue</sym>
+    <extensions>
+      <gpxx:WaypointExtension>
+        <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
+        <gpxx:Categories>
+          <gpxx:Category>Slow food</gpxx:Category>
+          <gpxx:Category>Category 11</gpxx:Category>
+        </gpxx:Categories>
+      </gpxx:WaypointExtension>
+    </extensions>
+  </wpt>
+  <wpt lat="40.003967283" lon="-105.498962400">
+    <time>2013-03-09T20:45:02Z</time>
+    <name>Hwy 72</name>
+    <cmt>The Peak to Peak</cmt>
+    <desc>The Peak to Peak</desc>
+    <sym>Flag, Blue</sym>
+    <extensions>
+      <gpxx:WaypointExtension>
+        <gpxx:DisplayMode>SymbolAndName</gpxx:DisplayMode>
+      </gpxx:WaypointExtension>
+    </extensions>
+  </wpt>
+</gpx>
diff --git a/reference/garmincategories.txt b/reference/garmincategories.txt
new file mode 100644 (file)
index 0000000..b7ec529
--- /dev/null
@@ -0,0 +1,7 @@
+Grid   Lat/Lon hddd°mm.mmm'\r
+Datum  WGS 84\r
+\r
+Header Name    Description     Type    Position        Altitude        Depth   Proximity       Temperature     Display Mode    Color   Symbol  Facility        City    State   Country Date Modified   Link    Categories\r
+\r
+Waypoint       Hwy 119 The Diagonal    User Waypoint   N39 58.432183 W105 27.951022                                    Symbol & Name   Unknown Flag, Blue                                      09.03.2013 13:45:12 PM          Slow food,Category 11\r
+Waypoint       Hwy 72  The Peak to Peak        User Waypoint   N40 00.238037 W105 29.937744                                    Symbol & Name   Unknown Flag, Blue                                      09.03.2013 13:45:02 PM          \r
index 85581adf413943427b433c3e266631ce3bfbd77b..f917b2a2e3cd9ba2dc0ae9ec8816dc93aa0689d0 100644 (file)
@@ -79,6 +79,13 @@ compare ${REFERENCE}/gpxpassthrough10~gpx.gpx ${TMPDIR}/gpxpassthrough10~gpx.gpx
 gpsbabel -i gpx -f ${REFERENCE}/gpxpassthrough11.gpx -o gpx -F ${TMPDIR}/gpxpassthrough11~gpx.gpx
 compare ${REFERENCE}/gpxpassthrough11~gpx.gpx ${TMPDIR}/gpxpassthrough11~gpx.gpx
 
+# garmin specific categories
+gpsbabel -p gpsbabel-sample.ini -i gpx -f ${REFERENCE}/garmincategories.gpx  -o garmin_txt,utc=-7 -F ${TMPDIR}/garmincategories~gpx.txt
+compare ${REFERENCE}/garmincategories.txt ${TMPDIR}/garmincategories~gpx.txt
+
+gpsbabel -p gpsbabel-sample.ini -i garmin_txt,utc=-7 -f ${REFERENCE}/garmincategories.txt -o gpx,garminextensions -F ${TMPDIR}/garmincategories~txt.gpx
+compare ${REFERENCE}/garmincategories.gpx ${TMPDIR}/garmincategories~txt.gpx
+
 if [ -z "${VALGRIND}" ]; then
   set -e
   if command -v xmllint > /dev/null;
index 92ca850431f7eaca1ee2afa83c8919e5062fe39e..679c19ad4683f2f411b6dd2149b345d212f94005 100644 (file)
--- a/unicsv.cc
+++ b/unicsv.cc
@@ -42,7 +42,7 @@
 #include "defs.h"
 #include "csv_util.h"              // for csv_linesplit, human_to_dec
 #include "formspec.h"              // for FormatSpecificDataList
-#include "garmin_fs.h"             // for garmin_fs_flags_t, garmin_fs_t, GMSD_GET, GMSD_HAS, GMSD_SETQSTR, GMSD_FIND, garmin_fs_alloc
+#include "garmin_fs.h"             // for garmin_fs_t
 #include "garmin_tables.h"         // for gt_lookup_datum_index, gt_get_mps_grid_longname, gt_lookup_grid_type
 #include "geocache.h"              // for Geocache, Geocache::status_t, Geoc...
 #include "jeeps/gpsmath.h"         // for GPS_Math_UKOSMap_To_WGS84_M, GPS_Math_EN_To_UKOSNG_Map, GPS_Math_Known_Datum_To_UTM_EN, GPS_Math_Known_Datum_To_WGS84_M, GPS_Math_Swiss_EN_To_WGS84, GPS_Math_UTM_EN_To_Known_Datum, GPS_Math_WGS84_To_Known_Datum_M, GPS_Math_WGS84_To_Swiss_EN, GPS_Math_WGS...
@@ -823,7 +823,7 @@ UnicsvFormat::unicsv_parse_one_line(const QString& ibuf)
     case fld_garmin_facility:
       gmsd = garmin_fs_t::find(wpt);
       if (! gmsd) {
-        gmsd = garmin_fs_alloc(-1);
+        gmsd = new garmin_fs_t(-1);
         wpt->fs.FsChainAdd(gmsd);
       }
       switch (unicsv_fields_tab[column]) {
@@ -1113,7 +1113,7 @@ void
 UnicsvFormat::unicsv_waypt_enum_cb(const Waypoint* wpt)
 {
   const QString& shortname = wpt->shortname;
-  garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+  const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
   if (!shortname.isEmpty()) {
     unicsv_outp_flags[fld_shortname] = true;
@@ -1264,7 +1264,7 @@ UnicsvFormat::unicsv_waypt_disp_cb(const Waypoint* wpt)
   unicsv_waypt_ct++;
 
   QString shortname = wpt->shortname;
-  garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+  const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
 
   if (unicsv_datum_idx == kDautmWGS84) {
     lat = wpt->latitude;
diff --git a/util.cc b/util.cc
index e1723f533ee3d133ba6dd0e316bc2e70d96ca2b3..c625e95d4884090d5f9f19a5f48841b8d1e84483 100644 (file)
--- a/util.cc
+++ b/util.cc
@@ -623,7 +623,7 @@ convert_human_date_format(const char* human_datef)
     }
 
     if (okay == 0) {
-      fatal("Invalid character \"%c\" in date format!", *cin);
+      fatal("Invalid character \"%c\" in date format \"%s\"!\n", *cin, human_datef);
     }
   }
   QString rv(result);
@@ -717,7 +717,7 @@ convert_human_time_format(const char* human_timef)
     }
 
     if (okay == 0) {
-      fatal("Invalid character \"%c\" in time format!", *cin);
+      fatal("Invalid character \"%c\" in time format \"%s\"!\n", *cin, human_timef);
     }
   }
   QString rv(result);
index 8130add755c97b80d589c95a03cdf8b6518df3aa..77a219faa230cd8a760581fbb08fab7b36a9bccc 100644 (file)
--- a/waypt.cc
+++ b/waypt.cc
@@ -230,22 +230,20 @@ double
 waypt_distance_ex(const Waypoint* A, const Waypoint* B)
 {
   double res = 0;
-  garmin_fs_t* gmsd;
 
   if ((A == nullptr) || (B == nullptr)) {
     return 0;
   }
 
-  if ((gmsd = garmin_fs_t::find(A)) && (gmsd->ilinks != nullptr)) {
-    garmin_ilink_t* link = gmsd->ilinks;
-
-    res = gcgeodist(A->latitude, A->longitude, link->lat, link->lon);
-    while (link->next != nullptr) {
-      garmin_ilink_t* prev = link;
-      link = link->next;
-      res += gcgeodist(prev->lat, prev->lon, link->lat, link->lon);
+  if (const garmin_fs_t* gmsd = garmin_fs_t::find(A); (gmsd != nullptr) && (!gmsd->ilinks.isEmpty())) {
+    auto prev_lat = A->latitude;
+    auto prev_lon = A->longitude;
+    for (const auto& link : gmsd->ilinks) {
+      res += gcgeodist(prev_lat, prev_lon, link.lat, link.lon);
+      prev_lat = link.lat;
+      prev_lon = link.lon;
     }
-    res += gcgeodist(link->lat, link->lon, B->latitude, B->longitude);
+    res += gcgeodist(gmsd->ilinks.last().lat, gmsd->ilinks.last().lon, B->latitude, B->longitude);
   } else {
     res = gcgeodist(A->latitude, A->longitude, B->latitude, B->longitude);
   }
diff --git a/xcsv.cc b/xcsv.cc
index d2c57e549f097224b8cbf199dc3f6865999fe2ea..d649fff555c6622b83876fb50ca37a0b80b149fb 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -51,7 +51,7 @@
 #include "defs.h"
 #include "csv_util.h"              // for csv_stringtrim, dec_to_human, csv_stringclean, human_to_dec, ddmmdir_to_degrees, dec_to_intdeg, decdir_to_dec, intdeg_to_dec, csv_linesplit
 #include "formspec.h"              // for FormatSpecificDataList
-#include "garmin_fs.h"             // for garmin_fs_t, garmin_fs_alloc
+#include "garmin_fs.h"             // for garmin_fs_t
 #include "geocache.h"              // for Geocache, Geocache::status_t, Geoc...
 #include "grtcirc.h"               // for RAD, gcdist, radtometers
 #include "jeeps/gpsmath.h"         // for GPS_Math_WGS84_To_UTM_EN, GPS_Lookup_Datum_Index, GPS_Math_Known_Datum_To_WGS84_M, GPS_Math_UTM_EN_To_Known_Datum, GPS_Math_WGS84_To_Known_Datum_M, GPS_Math_WGS84_To_UKOSMap_M
@@ -361,7 +361,7 @@ XcsvFormat::gmsd_init(Waypoint* wpt)
 {
   garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
   if (gmsd == nullptr) {
-    gmsd = garmin_fs_alloc(-1);
+    gmsd = new garmin_fs_t(-1);
     wpt->fs.FsChainAdd(gmsd);
   }
   return gmsd;
@@ -1534,42 +1534,42 @@ XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt)
     break;
     /* GMSD ************************************************************/
     case XcsvStyle::XT_COUNTRY: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_country(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_STATE: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_state(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_CITY: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_city(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_POSTAL_CODE: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_postal_code(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_STREET_ADDR: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_addr(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_PHONE_NR: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_phone_nr(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_FACILITY: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_facility(gmsd, "")));
     }
     break;
     case XcsvStyle::XT_EMAIL: {
-      garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
+      const garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
       buff = QString::asprintf(fmp.printfc.constData(), CSTR(garmin_fs_t::get_email(gmsd, "")));
     }
     break;